/**
 * 
 */
package com.app.gameserver;

import java.net.InetSocketAddress;
import java.nio.ByteOrder;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.HeapChannelBufferFactory;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.execution.MemoryAwareThreadPoolExecutor;
import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;

import com.app.db.DBPool;
import com.app.framework.AbstractServer;
import com.app.framework.config.ActionConfigLoader;
import com.app.framework.config.PropConfigLoader;
import com.app.framework.config.ServiceConfigLoader;
import com.app.framework.handler.GameUpstreamHandler;
import com.app.framework.handler.SocketUpstreamHandler;
import com.app.framework.pipelinefactory.GamePipelineFactory;
import com.app.framework.pipelinefactory.SocketPipelineFactory;
import com.app.framework.socket.client.ServerClient;
import com.app.service.item.itemspec.ItemSpecManager;

/**
 * @author lisong
 * @version 2013-2-26 下午2:41:32
 */
public class SocketServer extends AbstractServer {

	private static ServerClient client;
	
	private SocketServer() {
		
	}
	public static void start() throws Exception {
		
		ServiceConfigLoader.getInstance();
			
		ActionConfigLoader.getInstance();
		
		PropConfigLoader.getInstance();
		
		ItemSpecManager.getInstance();
		
		ServerBootstrap bootstrap=new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));
		
		OrderedMemoryAwareThreadPoolExecutor executor = new OrderedMemoryAwareThreadPoolExecutor(200, 1073741824, 1073741824*5000,30,TimeUnit.SECONDS,new ThreadFactory() {
			
			@Override
			public Thread newThread(Runnable r) {
				Thread t = new Thread(r);
				t.setName("pool-server2client-"+t.getId());
				return t;
			}
		});//两个内存参数分别为：单个channel的内存上限，和所有channal总和的内存上限。当单个channel内存上限达到以后，会关闭channel的读事件。当总内存达到上限以后execution中无法再添加任务（即消息在解码以后无法再执行到业务handler）一直等待（占用着work线程），直到总内存占用低于限定值
		
		GamePipelineFactory factory = new SocketPipelineFactory(executor,new SocketUpstreamHandler(GameUpstreamHandler.HANDLER_TYPE_CS,false));
		
		bootstrap.setPipelineFactory(factory);
		 
		int port = Integer.parseInt(PropConfigLoader.getInstance().getConfig("socketport").getValue());

		bootstrap.setOption("child.tcpNoDelay", true);//child的属性 是与客户端建立连接的socket的,非child的是服务器负责监听客户端连接的主serversocket的
		bootstrap.setOption("child.soLinger", 1);//设置solinger以后close会等到发送的数据已经确认了才返回。但是如果对方宕机，超时，那么会根据linger设定的时间（单位为秒）返回
		/**
		 * sendBufferSize如果太小，对于往外发东西的server而言，如果碰到其中某x台client接收慢，可能会很快导致send buffer满，这个时候会写不了，对于像mina/netty等框架而言，通常而言在写不了的情况下会放入队列，但不幸的是通常这都是一个没有限制大小的队列，所以有些时候在这种情况下可能会导致OOM，因此在写这块代码时要特别注意对内存的保护（宁愿写失败也不能导致OOM）
		 */
//		bootstrap.setOption("child.receiveBufferSize", 2048);//使用默认设置
		// Setting this option to a value such as 200, 500 or 1000, tells the TCP stack how long the "accept" queue can be. 
		//If this option is not configured, then the backlog depends on OS setting.
		bootstrap.setOption("backlog", 5000);//只有当服务器进程通过ServerSocket的accept()方法从队列中取出连接请求，使队列腾出空位时，队列才能继续加入新的连接请求，backlog即指定该等待队列的大小
		//http://blog.csdn.net/huang_xw/article/details/7338487
		Channel c = bootstrap.bind(new InetSocketAddress(port));
		c.getConfig().setBufferFactory(HeapChannelBufferFactory.getInstance(ByteOrder.BIG_ENDIAN));//默认大头,在此可设置
		
		System.out.println("socket server start on "+port);
		
		startClient();
	}
	
	private static void startClient() {
		
		 String wordIp = PropConfigLoader.getInstance().getConfig("worldIp").getValue();
		 int worldPort = Integer.parseInt(PropConfigLoader.getInstance().getConfig("wordPort").getValue());
		 if(worldPort > 0) {
			 int serverId = Integer.parseInt(PropConfigLoader.getInstance().getConfig("serverId").getValue());
			 int serverType = Integer.parseInt(PropConfigLoader.getInstance().getConfig("serverType").getValue());
			 MemoryAwareThreadPoolExecutor executor = new MemoryAwareThreadPoolExecutor(100,1073741824,1073741824*5000,30,TimeUnit.SECONDS,new ThreadFactory() {
				@Override
				public Thread newThread(Runnable r) {
					Thread t = new Thread(r);
					t.setName("pool-server2world-"+t.getId());
					return t;
				}
			});
			 client = new ServerClient(wordIp, worldPort, serverId, serverType, executor, new SocketUpstreamHandler(GameUpstreamHandler.HANDLER_TYPE_SS,true));
		 }
	}
	
	public static ServerClient getClient() {
		
		return client;
	}
	
	public static void main(String[] args) {
		try {
			SocketServer.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			@Override
			public void run() {
				DBPool.getInstance().close();
				client.close();
				client = null;
			}
		}));
	}
}
